﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using Cloak.Reflection;

namespace Ruleweaver.Rules.CheckMethods
{
	public sealed class ContainsMethod : CheckMethod
	{
		private readonly object _value;
		private readonly LinqResolution? _resolution;

		public ContainsMethod(object value)
		{
			_value = value;
		}

		public ContainsMethod(object value, LinqResolution resolution) : this(value)
		{
			_resolution = resolution;
		}

		protected override bool SupportsTargetType(Type targetType)
		{
			return SupportsStringContains(targetType) || SupportsSequenceContains(targetType);
		}

		private bool SupportsStringContains(Type targetType)
		{
			return targetType == typeof(string) && _value is string;
		}

		private bool SupportsSequenceContains(Type targetType)
		{
			var supported = false;

			if(targetType.IsGenericType)
			{
				var openTargetType = targetType.GetGenericTypeDefinition();

				if(typeof(IEnumerable<>).IsAssignableFrom(openTargetType))
				{
					var elementType = openTargetType.GetGenericArguments().Single();

					supported = (_value == null && elementType.IsAssignableNull()) || elementType.IsAssignableFrom(_value.GetType());
				}
			}

			return supported;
		}

		protected override MethodInfo GetMethod(Type targetType, Type checkType)
		{
			return GetCheckMethod(targetType).GetRule(targetType).Method;
		}

		protected override IEnumerable<object> GetCheckArguments(Type targetType)
		{
			return GetCheckMethod(targetType).GetRule(targetType).CheckArguments;
		}

		private ICheckMethod GetCheckMethod(Type targetType)
		{
			if(targetType == typeof(string))
			{
				return new StringContainsMethod((string) _value);
			}
			else if(_resolution != null)
			{
				return new SequenceContainsMethod(_value, _resolution.Value);
			}
			else
			{
				return new SequenceContainsMethod(_value, default(LinqResolution));
			}
		}
	}
}